package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper accountFreezeMapper;

    @Override
    @Transactional
    public void deduct(String userId, int money) {
        //1.因为数据库表中money字段类型是unsigned格式，意思是不能为负数，如果为负数，会直接报错事务直接回滚
        //获取事务id
        String xid = RootContext.getXID();
        //判断freeze中是否有冻结记录，如果有记录，如果有，那一定是cancel执行过了，我们要拒绝业务
        AccountFreeze accountFreeze = accountFreezeMapper.selectById(xid);
        if (accountFreeze != null) {
            //cancel执行过了，我们要拒绝业务
            return;
        }
        //扣减可用余额
        accountMapper.deduct(userId, money);
        //记录冻结金额，事务状态
        AccountFreeze freeze = new AccountFreeze();
        freeze.setXid(xid);
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        accountFreezeMapper.insert(freeze);
    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {
        //获取事务id删除冻结金额记录即可
        String xid = ctx.getXid();
        //count是删除的条数，如果等于1就表示删除成功了
        int count = accountFreezeMapper.deleteById(xid);
        return count == 1;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        //查询冻结记录
        String xid = ctx.getXid();
        String userId = ctx.getActionContext("userId").toString();
        AccountFreeze freeze = accountFreezeMapper.selectById(xid);
        //做一下空回滚的判断，判断freeze是否为null，如果为null证明try没执行，需要空回滚
        if (freeze == null) {
            freeze = new AccountFreeze();
            freeze.setXid(xid);
            freeze.setUserId(userId);
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            accountFreezeMapper.insert(freeze);
            return true;
        }
        //幂等判断
        if (freeze.getState() == AccountFreeze.State.CANCEL) {
            //如果相等，表示已经处理过一次cancel，无需再重复处理了
            return true;
        }
        //恢复可用余额
        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());
        //将冻结金额清零，不是删除因为要判断空回滚用，状态改为cancel
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = accountFreezeMapper.updateById(freeze);
        return count == 1;
    }
}
